home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gsalloc.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  58.4 KB  |  1,986 lines

  1. /* Copyright (C) 1995, 2000 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gsalloc.c,v 1.8 2000/09/19 19:00:25 lpd Exp $ */
  20. /* Standard memory allocator */
  21. #include "gx.h"
  22. #include "memory_.h"
  23. #include "gserrors.h"
  24. #include "gsexit.h"
  25. #include "gsmdebug.h"
  26. #include "gsstruct.h"
  27. #include "gxalloc.h"
  28. #include "stream.h"        /* for clearing stream list */
  29.  
  30. /*
  31.  * This allocator produces tracing messages of the form
  32.  *      [aNMOTS]...
  33.  * where
  34.  *   N is the VM space number, +1 if we are allocating from stable memory.
  35.  *   M is : for movable objects, | for immovable,
  36.  *   O is {alloc = +, free = -, grow = >, shrink = <},
  37.  *   T is {bytes = b, object = <, ref = $, string = >}, and
  38.  *   S is {small freelist = f, large freelist = F, LIFO = space,
  39.  *      own chunk = L, lost = #, lost own chunk = ~, other = .}.
  40.  */
  41. #ifdef DEBUG
  42. private int
  43. alloc_trace_space(const gs_ref_memory_t *imem)
  44. {
  45.     return imem->space + (imem->stable_memory == (const gs_memory_t *)imem);
  46. }
  47. private void
  48. alloc_trace(const char *chars, gs_ref_memory_t * imem, client_name_t cname,
  49.         gs_memory_type_ptr_t stype, uint size, const void *ptr)
  50. {
  51.     if_debug7('A', "[a%d%s]%s %s(%u) %s0x%lx\n",
  52.           alloc_trace_space(imem), chars, client_name_string(cname),
  53.           (ptr == 0 || stype == 0 ? "" :
  54.            struct_type_name_string(stype)),
  55.           size, (chars[1] == '+' ? "= " : ""), (ulong) ptr);
  56. }
  57. private bool
  58. alloc_size_is_ok(gs_memory_type_ptr_t stype)
  59. {
  60.     return (stype->ssize > 0 && stype->ssize < 0x100000);
  61. }
  62. #  define ALLOC_CHECK_SIZE(stype)\
  63.     BEGIN\
  64.       if (!alloc_size_is_ok(stype)) {\
  65.     lprintf2("size of struct type 0x%lx is 0x%lx!\n",\
  66.          (ulong)(stype), (ulong)((stype)->ssize));\
  67.     return 0;\
  68.       }\
  69.     END
  70. #else
  71. #  define alloc_trace(chars, imem, cname, stype, size, ptr) DO_NOTHING
  72. #  define ALLOC_CHECK_SIZE(stype) DO_NOTHING
  73. #endif
  74.  
  75. /*
  76.  * The structure descriptor for allocators.  Even though allocators
  77.  * are allocated outside GC space, they reference objects within it.
  78.  */
  79. public_st_ref_memory();
  80. private 
  81. ENUM_PTRS_BEGIN(ref_memory_enum_ptrs) return 0;
  82. ENUM_PTR3(0, gs_ref_memory_t, streams, names_array, changes);
  83. ENUM_PTR(3, gs_ref_memory_t, saved);
  84. ENUM_PTRS_END
  85. private RELOC_PTRS_WITH(ref_memory_reloc_ptrs, gs_ref_memory_t *mptr)
  86. {
  87.     RELOC_PTR(gs_ref_memory_t, streams);
  88.     RELOC_PTR(gs_ref_memory_t, names_array);
  89.     RELOC_PTR(gs_ref_memory_t, changes);
  90.     /* Don't relocate the saved pointer now -- see igc.c for details. */
  91.     mptr->reloc_saved = RELOC_OBJ(mptr->saved);
  92. }
  93. RELOC_PTRS_END
  94.  
  95. /*
  96.  * Define flags for the alloc_obj, which implements all but the fastest
  97.  * case of allocation.
  98.  */
  99. typedef enum {
  100.     ALLOC_IMMOVABLE = 1,
  101.     ALLOC_DIRECT = 2        /* called directly, without fast-case checks */
  102. } alloc_flags_t;
  103.  
  104. /* Forward references */
  105. private void remove_range_from_freelist(P3(gs_ref_memory_t *mem, void* bottom, void* top));
  106. private obj_header_t *large_freelist_alloc(P2(gs_ref_memory_t *mem, uint size));
  107. private obj_header_t *scavenge_low_free(P2(gs_ref_memory_t *mem, unsigned request_size));
  108. private ulong compute_free_objects(P1(gs_ref_memory_t *));
  109. private obj_header_t *alloc_obj(P5(gs_ref_memory_t *, ulong, gs_memory_type_ptr_t, alloc_flags_t, client_name_t));
  110. private void consolidate_chunk_free(P2(chunk_t *cp, gs_ref_memory_t *mem));
  111. private void trim_obj(P4(gs_ref_memory_t *mem, obj_header_t *obj, uint size, chunk_t *cp));
  112. private chunk_t *alloc_acquire_chunk(P4(gs_ref_memory_t *, ulong, bool, client_name_t));
  113. private chunk_t *alloc_add_chunk(P3(gs_ref_memory_t *, ulong, client_name_t));
  114. void alloc_close_chunk(P1(gs_ref_memory_t *));
  115.  
  116. /*
  117.  * Define the standard implementation (with garbage collection)
  118.  * of Ghostscript's memory manager interface.
  119.  */
  120. /* Raw memory procedures */
  121. private gs_memory_proc_alloc_bytes(i_alloc_bytes_immovable);
  122. private gs_memory_proc_resize_object(i_resize_object);
  123. private gs_memory_proc_free_object(i_free_object);
  124. private gs_memory_proc_stable(i_stable);
  125. private gs_memory_proc_status(i_status);
  126. private gs_memory_proc_free_all(i_free_all);
  127. private gs_memory_proc_consolidate_free(i_consolidate_free);
  128.  
  129. /* Object memory procedures */
  130. private gs_memory_proc_alloc_bytes(i_alloc_bytes);
  131. private gs_memory_proc_alloc_struct(i_alloc_struct);
  132. private gs_memory_proc_alloc_struct(i_alloc_struct_immovable);
  133. private gs_memory_proc_alloc_byte_array(i_alloc_byte_array);
  134. private gs_memory_proc_alloc_byte_array(i_alloc_byte_array_immovable);
  135. private gs_memory_proc_alloc_struct_array(i_alloc_struct_array);
  136. private gs_memory_proc_alloc_struct_array(i_alloc_struct_array_immovable);
  137. private gs_memory_proc_object_size(i_object_size);
  138. private gs_memory_proc_object_type(i_object_type);
  139. private gs_memory_proc_alloc_string(i_alloc_string);
  140. private gs_memory_proc_alloc_string(i_alloc_string_immovable);
  141. private gs_memory_proc_resize_string(i_resize_string);
  142. private gs_memory_proc_free_string(i_free_string);
  143. private gs_memory_proc_register_root(i_register_root);
  144. private gs_memory_proc_unregister_root(i_unregister_root);
  145. private gs_memory_proc_enable_free(i_enable_free);
  146.  
  147. /* We export the procedures for subclasses. */
  148. const gs_memory_procs_t gs_ref_memory_procs =
  149. {
  150.     /* Raw memory procedures */
  151.     i_alloc_bytes_immovable,
  152.     i_resize_object,
  153.     i_free_object,
  154.     i_stable,
  155.     i_status,
  156.     i_free_all,
  157.     i_consolidate_free,
  158.     /* Object memory procedures */
  159.     i_alloc_bytes,
  160.     i_alloc_struct,
  161.     i_alloc_struct_immovable,
  162.     i_alloc_byte_array,
  163.     i_alloc_byte_array_immovable,
  164.     i_alloc_struct_array,
  165.     i_alloc_struct_array_immovable,
  166.     i_object_size,
  167.     i_object_type,
  168.     i_alloc_string,
  169.     i_alloc_string_immovable,
  170.     i_resize_string,
  171.     i_free_string,
  172.     i_register_root,
  173.     i_unregister_root,
  174.     i_enable_free
  175. };
  176.  
  177. /*
  178.  * Allocate and mostly initialize the state of an allocator (system, global,
  179.  * or local).  Does not initialize global or space.
  180.  */
  181. private void *ialloc_solo(P3(gs_raw_memory_t *, gs_memory_type_ptr_t,
  182.                  chunk_t **));
  183. gs_ref_memory_t *
  184. ialloc_alloc_state(gs_raw_memory_t * parent, uint chunk_size)
  185. {
  186.     chunk_t *cp;
  187.     gs_ref_memory_t *iimem = ialloc_solo(parent, &st_ref_memory, &cp);
  188.  
  189.     if (iimem == 0)
  190.     return 0;
  191.     iimem->stable_memory = (gs_memory_t *)iimem;
  192.     iimem->procs = gs_ref_memory_procs;
  193.     iimem->parent = parent;
  194.     iimem->chunk_size = chunk_size;
  195.     iimem->large_size = ((chunk_size / 4) & -obj_align_mod) + 1;
  196.     iimem->is_controlled = false;
  197.     iimem->gc_status.vm_threshold = chunk_size * 3L;
  198.     iimem->gc_status.max_vm = max_long;
  199.     iimem->gc_status.psignal = NULL;
  200.     iimem->gc_status.enabled = false;
  201.     iimem->previous_status.allocated = 0;
  202.     iimem->previous_status.used = 0;
  203.     ialloc_reset(iimem);
  204.     iimem->cfirst = iimem->clast = cp;
  205.     ialloc_set_limit(iimem);
  206.     iimem->cc.cbot = iimem->cc.ctop = 0;
  207.     iimem->pcc = 0;
  208.     iimem->save_level = 0;
  209.     iimem->new_mask = 0;
  210.     iimem->test_mask = ~0;
  211.     iimem->streams = 0;
  212.     iimem->names_array = 0;
  213.     iimem->roots = 0;
  214.     iimem->num_contexts = 0;
  215.     iimem->saved = 0;
  216.     return iimem;
  217. }
  218.  
  219. /* Allocate a 'solo' object with its own chunk. */
  220. private void *
  221. ialloc_solo(gs_raw_memory_t * parent, gs_memory_type_ptr_t pstype,
  222.         chunk_t ** pcp)
  223. {    /*
  224.      * We can't assume that the parent uses the same object header
  225.      * that we do, but the GC requires that allocators have
  226.      * such a header.  Therefore, we prepend one explicitly.
  227.      */
  228.     chunk_t *cp =
  229.     gs_raw_alloc_struct_immovable(parent, &st_chunk,
  230.                       "ialloc_solo(chunk)");
  231.     uint csize =
  232.     ROUND_UP(sizeof(chunk_head_t) + sizeof(obj_header_t) +
  233.          pstype->ssize,
  234.          obj_align_mod);
  235.     byte *cdata = gs_alloc_bytes_immovable(parent, csize, "ialloc_solo");
  236.     obj_header_t *obj = (obj_header_t *) (cdata + sizeof(chunk_head_t));
  237.  
  238.     if (cp == 0 || cdata == 0)
  239.     return 0;
  240.     alloc_init_chunk(cp, cdata, cdata + csize, false, (chunk_t *) NULL);
  241.     cp->cbot = cp->ctop;
  242.     cp->cprev = cp->cnext = 0;
  243.     /* Construct the object header "by hand". */
  244.     obj->o_alone = 1;
  245.     obj->o_size = pstype->ssize;
  246.     obj->o_type = pstype;
  247.     *pcp = cp;
  248.     return (void *)(obj + 1);
  249. }
  250.  
  251. /*
  252.  * Add a chunk to an externally controlled allocator.  Such allocators
  253.  * allocate all objects as immovable, are not garbage-collected, and
  254.  * don't attempt to acquire additional memory on their own.
  255.  */
  256. int
  257. ialloc_add_chunk(gs_ref_memory_t *imem, ulong space, client_name_t cname)
  258. {
  259.     chunk_t *cp;
  260.  
  261.     /* Allow acquisition of this chunk. */
  262.     imem->is_controlled = false;
  263.     imem->large_size = imem->chunk_size;
  264.     imem->limit = max_long;
  265.     imem->gc_status.max_vm = max_long;
  266.  
  267.     /* Acquire the chunk. */
  268.     cp = alloc_add_chunk(imem, space, cname);
  269.  
  270.     /*
  271.      * Make all allocations immovable.  Since the "movable" allocators
  272.      * allocate within existing chunks, whereas the "immovable" ones
  273.      * allocate in new chunks, we equate the latter to the former, even
  274.      * though this seems backwards.
  275.      */
  276.     imem->procs.alloc_bytes_immovable = imem->procs.alloc_bytes;
  277.     imem->procs.alloc_struct_immovable = imem->procs.alloc_struct;
  278.     imem->procs.alloc_byte_array_immovable = imem->procs.alloc_byte_array;
  279.     imem->procs.alloc_struct_array_immovable = imem->procs.alloc_struct_array;
  280.     imem->procs.alloc_string_immovable = imem->procs.alloc_string;
  281.  
  282.     /* Disable acquisition of additional chunks. */
  283.     imem->is_controlled = true;
  284.     imem->limit = 0;
  285.  
  286.     return (cp ? 0 : gs_note_error(gs_error_VMerror));
  287. }
  288.  
  289. /* Prepare for a GC by clearing the stream list. */
  290. /* This probably belongs somewhere else.... */
  291. void
  292. ialloc_gc_prepare(gs_ref_memory_t * mem)
  293. {    /*
  294.      * We have to unlink every stream from its neighbors,
  295.      * so that referenced streams don't keep all streams around.
  296.      */
  297.     while (mem->streams != 0) {
  298.     stream *s = mem->streams;
  299.  
  300.     mem->streams = s->next;
  301.     s->prev = s->next = 0;
  302.     }
  303. }
  304.  
  305. /* Initialize after a save. */
  306. void
  307. ialloc_reset(gs_ref_memory_t * mem)
  308. {
  309.     mem->cfirst = 0;
  310.     mem->clast = 0;
  311.     mem->cc.rcur = 0;
  312.     mem->cc.rtop = 0;
  313.     mem->cc.has_refs = false;
  314.     mem->allocated = 0;
  315.     mem->inherited = 0;
  316.     mem->changes = 0;
  317.     ialloc_reset_free(mem);
  318. }
  319.  
  320. /* Initialize after a save or GC. */
  321. void
  322. ialloc_reset_free(gs_ref_memory_t * mem)
  323. {
  324.     int i;
  325.     obj_header_t **p;
  326.  
  327.     mem->lost.objects = 0;
  328.     mem->lost.refs = 0;
  329.     mem->lost.strings = 0;
  330.     mem->cfreed.cp = 0;
  331.     for (i = 0, p = &mem->freelists[0]; i < num_freelists; i++, p++)
  332.     *p = 0;
  333.     mem->largest_free_size = 0;
  334. }
  335.  
  336. /* Set the allocation limit after a change in one or more of */
  337. /* vm_threshold, max_vm, or enabled, or after a GC. */
  338. void
  339. ialloc_set_limit(register gs_ref_memory_t * mem)
  340. {    /*
  341.      * The following code is intended to set the limit so that
  342.      * we stop allocating when allocated + previous_status.allocated
  343.      * exceeds the lesser of max_vm or (if GC is enabled)
  344.      * gc_allocated + vm_threshold.
  345.      */
  346.     ulong max_allocated =
  347.     (mem->gc_status.max_vm > mem->previous_status.allocated ?
  348.      mem->gc_status.max_vm - mem->previous_status.allocated :
  349.      0);
  350.  
  351.     if (mem->gc_status.enabled) {
  352.     ulong limit = mem->gc_allocated + mem->gc_status.vm_threshold;
  353.  
  354.     if (limit < mem->previous_status.allocated)
  355.         mem->limit = 0;
  356.     else {
  357.         limit -= mem->previous_status.allocated;
  358.         mem->limit = min(limit, max_allocated);
  359.     }
  360.     } else
  361.     mem->limit = max_allocated;
  362.     if_debug7('0', "[0]space=%d, max_vm=%ld, prev.alloc=%ld, enabled=%d,\n\
  363.       gc_alloc=%ld, threshold=%ld => limit=%ld\n",
  364.           mem->space, (long)mem->gc_status.max_vm,
  365.           (long)mem->previous_status.allocated,
  366.           mem->gc_status.enabled, (long)mem->gc_allocated,
  367.           (long)mem->gc_status.vm_threshold, (long)mem->limit);
  368. }
  369.  
  370. /*
  371.  * Free all the memory owned by the allocator, except the allocator itself.
  372.  * Note that this only frees memory at the current save level: the client
  373.  * is responsible for restoring to the outermost level if desired.
  374.  */
  375. private void
  376. i_free_all(gs_memory_t * mem, uint free_mask, client_name_t cname)
  377. {
  378.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  379.     chunk_t *cp;
  380.  
  381.     if (free_mask & FREE_ALL_DATA) {
  382.     chunk_t *csucc;
  383.  
  384.     /*
  385.      * Free the chunks in reverse order, to encourage LIFO behavior.
  386.      * Don't free the chunk holding the allocator itself.
  387.      */
  388.     for (cp = imem->clast; cp != 0; cp = csucc) {
  389.         csucc = cp->cprev;    /* save before freeing */
  390.         if (cp->cbase + sizeof(obj_header_t) != (byte *)mem)
  391.         alloc_free_chunk(cp, imem);
  392.     }
  393.     }
  394.     if (free_mask & FREE_ALL_ALLOCATOR) {
  395.     /* Free the chunk holding the allocator itself. */
  396.     for (cp = imem->clast; cp != 0; cp = cp->cprev)
  397.         if (cp->cbase + sizeof(obj_header_t) == (byte *)mem) {
  398.         alloc_free_chunk(cp, imem);
  399.         break;
  400.         }
  401.     }
  402. }
  403.  
  404. /* ================ Accessors ================ */
  405.  
  406. /* Get the size of an object from the header. */
  407. private uint
  408. i_object_size(gs_memory_t * mem, const void /*obj_header_t */ *obj)
  409. {
  410.     return pre_obj_contents_size((const obj_header_t *)obj - 1);
  411. }
  412.  
  413. /* Get the type of a structure from the header. */
  414. private gs_memory_type_ptr_t
  415. i_object_type(gs_memory_t * mem, const void /*obj_header_t */ *obj)
  416. {
  417.     return ((const obj_header_t *)obj - 1)->o_type;
  418. }
  419.  
  420. /* Get the GC status of a memory. */
  421. void
  422. gs_memory_gc_status(const gs_ref_memory_t * mem, gs_memory_gc_status_t * pstat)
  423. {
  424.     *pstat = mem->gc_status;
  425. }
  426.  
  427. /* Set the GC status of a memory. */
  428. void
  429. gs_memory_set_gc_status(gs_ref_memory_t * mem, const gs_memory_gc_status_t * pstat)
  430. {
  431.     mem->gc_status = *pstat;
  432.     ialloc_set_limit(mem);
  433. }
  434.  
  435. /* ================ Objects ================ */
  436.  
  437. /* Allocate a small object quickly if possible. */
  438. /* The size must be substantially less than max_uint. */
  439. /* ptr must be declared as obj_header_t *. */
  440. /* pfl must be declared as obj_header_t **. */
  441. #define IF_FREELIST_ALLOC(ptr, imem, size, pstype, pfl)\
  442.     if ( size <= max_freelist_size &&\
  443.          *(pfl = &imem->freelists[(size + obj_align_mask) >> log2_obj_align_mod]) != 0\
  444.        )\
  445.     {    ptr = *pfl;\
  446.         *pfl = *(obj_header_t **)ptr;\
  447.         ptr[-1].o_size = size;\
  448.         ptr[-1].o_type = pstype;\
  449.         /* If debugging, clear the block in an attempt to */\
  450.         /* track down uninitialized data errors. */\
  451.         gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
  452. #define ELSEIF_BIG_FREELIST_ALLOC(ptr, imem, size, pstype)\
  453.     }\
  454.     else if (size > max_freelist_size &&\
  455.          (ptr = large_freelist_alloc(imem, size)) != 0)\
  456.     {    ptr[-1].o_type = pstype;\
  457.         /* If debugging, clear the block in an attempt to */\
  458.         /* track down uninitialized data errors. */\
  459.         gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
  460. #define ELSEIF_LIFO_ALLOC(ptr, imem, size, pstype)\
  461.     }\
  462.     else if ( (imem->cc.ctop - (byte *)(ptr = (obj_header_t *)imem->cc.cbot))\
  463.         >= size + (obj_align_mod + sizeof(obj_header_t) * 2) &&\
  464.          size < imem->large_size\
  465.        )\
  466.     {    imem->cc.cbot = (byte *)ptr + obj_size_round(size);\
  467.         ptr->o_alone = 0;\
  468.         ptr->o_size = size;\
  469.         ptr->o_type = pstype;\
  470.         ptr++;\
  471.         /* If debugging, clear the block in an attempt to */\
  472.         /* track down uninitialized data errors. */\
  473.         gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
  474. #define ELSE_ALLOC\
  475.     }\
  476.     else
  477.  
  478. private byte *
  479. i_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
  480. {
  481.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  482.     obj_header_t *obj;
  483.     obj_header_t **pfl;
  484.  
  485.     IF_FREELIST_ALLOC(obj, imem, size, &st_bytes, pfl)
  486.     alloc_trace(":+bf", imem, cname, NULL, size, obj);
  487.     ELSEIF_BIG_FREELIST_ALLOC(obj, imem, size, &st_bytes)
  488.     alloc_trace(":+bF", imem, cname, NULL, size, obj);
  489.     ELSEIF_LIFO_ALLOC(obj, imem, size, &st_bytes)
  490.     alloc_trace(":+b ", imem, cname, NULL, size, obj);
  491.     ELSE_ALLOC
  492.     {
  493.     obj = alloc_obj(imem, size, &st_bytes, 0, cname);
  494.     if (obj == 0)
  495.         return 0;
  496.     alloc_trace(":+b.", imem, cname, NULL, size, obj);
  497.     }
  498.     return (byte *) obj;
  499. }
  500. private byte *
  501. i_alloc_bytes_immovable(gs_memory_t * mem, uint size, client_name_t cname)
  502. {
  503.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  504.     obj_header_t *obj = alloc_obj(imem, size, &st_bytes,
  505.                   ALLOC_IMMOVABLE | ALLOC_DIRECT, cname);
  506.  
  507.     if (obj == 0)
  508.     return 0;
  509.     alloc_trace("|+b.", imem, cname, NULL, size, obj);
  510.     return (byte *) obj;
  511. }
  512. private void *
  513. i_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
  514.            client_name_t cname)
  515. {
  516.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  517.     uint size = pstype->ssize;
  518.     obj_header_t *obj;
  519.     obj_header_t **pfl;
  520.  
  521.     ALLOC_CHECK_SIZE(pstype);
  522.     IF_FREELIST_ALLOC(obj, imem, size, pstype, pfl)
  523.     alloc_trace(":+<f", imem, cname, pstype, size, obj);
  524.     ELSEIF_BIG_FREELIST_ALLOC(obj, imem, size, pstype)
  525.     alloc_trace(":+<F", imem, cname, pstype, size, obj);
  526.     ELSEIF_LIFO_ALLOC(obj, imem, size, pstype)
  527.     alloc_trace(":+< ", imem, cname, pstype, size, obj);
  528.     ELSE_ALLOC
  529.     {
  530.     obj = alloc_obj(imem, size, pstype, 0, cname);
  531.     if (obj == 0)
  532.         return 0;
  533.     alloc_trace(":+<.", imem, cname, pstype, size, obj);
  534.     }
  535.     return obj;
  536. }
  537. private void *
  538. i_alloc_struct_immovable(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
  539.              client_name_t cname)
  540. {
  541.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  542.     uint size = pstype->ssize;
  543.     obj_header_t *obj;
  544.  
  545.     ALLOC_CHECK_SIZE(pstype);
  546.     obj = alloc_obj(imem, size, pstype, ALLOC_IMMOVABLE | ALLOC_DIRECT, cname);
  547.     alloc_trace("|+<.", imem, cname, pstype, size, obj);
  548.     return obj;
  549. }
  550. private byte *
  551. i_alloc_byte_array(gs_memory_t * mem, uint num_elements, uint elt_size,
  552.            client_name_t cname)
  553. {
  554.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  555.     obj_header_t *obj = alloc_obj(imem, (ulong) num_elements * elt_size,
  556.                   &st_bytes, ALLOC_DIRECT, cname);
  557.  
  558.     if_debug6('A', "[a%d:+b.]%s -bytes-*(%lu=%u*%u) = 0x%lx\n",
  559.           alloc_trace_space(imem), client_name_string(cname),
  560.           (ulong) num_elements * elt_size,
  561.           num_elements, elt_size, (ulong) obj);
  562.     return (byte *) obj;
  563. }
  564. private byte *
  565. i_alloc_byte_array_immovable(gs_memory_t * mem, uint num_elements,
  566.                  uint elt_size, client_name_t cname)
  567. {
  568.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  569.     obj_header_t *obj = alloc_obj(imem, (ulong) num_elements * elt_size,
  570.                   &st_bytes, ALLOC_IMMOVABLE | ALLOC_DIRECT,
  571.                   cname);
  572.  
  573.     if_debug6('A', "[a%d|+b.]%s -bytes-*(%lu=%u*%u) = 0x%lx\n",
  574.           alloc_trace_space(imem), client_name_string(cname),
  575.           (ulong) num_elements * elt_size,
  576.           num_elements, elt_size, (ulong) obj);
  577.     return (byte *) obj;
  578. }
  579. private void *
  580. i_alloc_struct_array(gs_memory_t * mem, uint num_elements,
  581.              gs_memory_type_ptr_t pstype, client_name_t cname)
  582. {
  583.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  584.     obj_header_t *obj;
  585.  
  586.     ALLOC_CHECK_SIZE(pstype);
  587.     obj = alloc_obj(imem,
  588.             (ulong) num_elements * pstype->ssize,
  589.             pstype, ALLOC_DIRECT, cname);
  590.     if_debug7('A', "[a%d:+<.]%s %s*(%lu=%u*%u) = 0x%lx\n",
  591.           alloc_trace_space(imem), client_name_string(cname),
  592.           struct_type_name_string(pstype),
  593.           (ulong) num_elements * pstype->ssize,
  594.           num_elements, pstype->ssize, (ulong) obj);
  595.     return (char *)obj;
  596. }
  597. private void *
  598. i_alloc_struct_array_immovable(gs_memory_t * mem, uint num_elements,
  599.                gs_memory_type_ptr_t pstype, client_name_t cname)
  600. {
  601.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  602.     obj_header_t *obj;
  603.  
  604.     ALLOC_CHECK_SIZE(pstype);
  605.     obj = alloc_obj(imem,
  606.             (ulong) num_elements * pstype->ssize,
  607.             pstype, ALLOC_IMMOVABLE | ALLOC_DIRECT, cname);
  608.     if_debug7('A', "[a%d|+<.]%s %s*(%lu=%u*%u) = 0x%lx\n",
  609.           alloc_trace_space(imem), client_name_string(cname),
  610.           struct_type_name_string(pstype),
  611.           (ulong) num_elements * pstype->ssize,
  612.           num_elements, pstype->ssize, (ulong) obj);
  613.     return (char *)obj;
  614. }
  615. private void *
  616. i_resize_object(gs_memory_t * mem, void *obj, uint new_num_elements,
  617.         client_name_t cname)
  618. {
  619.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  620.     obj_header_t *pp = (obj_header_t *) obj - 1;
  621.     gs_memory_type_ptr_t pstype = pp->o_type;
  622.     ulong old_size = pre_obj_contents_size(pp);
  623.     ulong new_size = (ulong) pstype->ssize * new_num_elements;
  624.     ulong old_size_rounded = obj_align_round(old_size);
  625.     ulong new_size_rounded = obj_align_round(new_size);
  626.     void *new_obj = NULL;
  627.  
  628.     if (old_size_rounded == new_size_rounded)
  629.     return obj;
  630.     if ((byte *)obj + old_size_rounded == imem->cc.cbot &&
  631.     imem->cc.ctop - (byte *)obj >= new_size_rounded ) {
  632.     imem->cc.cbot = (byte *)obj + new_size_rounded;
  633.     pp->o_size = new_size;
  634.     new_obj = obj;
  635.     } else /* try and trim the object -- but only if room for a dummy header */
  636.     if (new_size_rounded + sizeof(obj_header_t) <= old_size_rounded) {
  637.         trim_obj(imem, obj, new_size_rounded, (chunk_t *)0);
  638.         new_obj = obj;
  639.     }
  640.     if (new_obj) {
  641.     if_debug8('A', "[a%d:%c%c ]%s %s(%lu=>%lu) 0x%lx\n",
  642.           alloc_trace_space(imem),
  643.           (new_size > old_size ? '>' : '<'),
  644.           (pstype == &st_bytes ? 'b' : '<'),
  645.           client_name_string(cname),
  646.           struct_type_name_string(pstype),
  647.           old_size, new_size, (ulong) obj);
  648.     return new_obj;
  649.     }
  650.     /* Punt. */
  651.     new_obj = gs_alloc_struct_array(mem, new_num_elements, void,
  652.                     pstype, cname);
  653.     if (new_obj == 0)
  654.     return 0;
  655.     memcpy(new_obj, obj, min(old_size, new_size));
  656.     gs_free_object(mem, obj, cname);
  657.     return new_obj;
  658. }
  659. private void
  660. i_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
  661. {
  662.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  663.     obj_header_t *pp;
  664.     gs_memory_type_ptr_t pstype;
  665.  
  666.     struct_proc_finalize((*finalize));
  667.     uint size, rounded_size;
  668.  
  669.     if (ptr == 0)
  670.     return;
  671.     pp = (obj_header_t *) ptr - 1;
  672.     pstype = pp->o_type;
  673. #ifdef DEBUG
  674.     if (gs_debug_c('?')) {
  675.     chunk_locator_t cld;
  676.  
  677.     if (pstype == &st_free) {
  678.         lprintf2("%s: object 0x%lx already free!\n",
  679.              client_name_string(cname), (ulong) ptr);
  680.         return;        /*gs_abort(); */
  681.     }
  682.     /* Check that this allocator owns the object being freed. */
  683.     cld.memory = imem;
  684.     while ((cld.cp = cld.memory->clast),
  685.            !chunk_locate_ptr(ptr, &cld)
  686.         ) {
  687.         if (!cld.memory->saved) {
  688.         lprintf3("%s: freeing 0x%lx, not owned by memory 0x%lx!\n",
  689.              client_name_string(cname), (ulong) ptr,
  690.              (ulong) mem);
  691.         return;        /*gs_abort(); */
  692.         }
  693.           /****** HACK: we know the saved state is the first ******
  694.            ****** member of an alloc_save_t. ******/
  695.         cld.memory = (gs_ref_memory_t *) cld.memory->saved;
  696.     }
  697.     /* Check that the object is in the allocated region. */
  698.     if (cld.memory == imem && cld.cp == imem->pcc)
  699.         cld.cp = &imem->cc;
  700.     if (!(PTR_BETWEEN((const byte *)pp, cld.cp->cbase,
  701.               cld.cp->cbot))
  702.         ) {
  703.         lprintf5("%s: freeing 0x%lx,\n\toutside chunk 0x%lx cbase=0x%lx, cbot=0x%lx!\n",
  704.              client_name_string(cname), (ulong) ptr,
  705.              (ulong) cld.cp, (ulong) cld.cp->cbase,
  706.              (ulong) cld.cp->cbot);
  707.         return;        /*gs_abort(); */
  708.     }
  709.     }
  710. #endif
  711.     size = pre_obj_contents_size(pp);
  712.     rounded_size = obj_align_round(size);
  713.     finalize = pstype->finalize;
  714.     if (finalize != 0) {
  715.     if_debug3('u', "[u]finalizing %s 0x%lx (%s)\n",
  716.           struct_type_name_string(pstype),
  717.           (ulong) ptr, client_name_string(cname));
  718.     (*finalize) (ptr);
  719.     }
  720.     if ((byte *) ptr + rounded_size == imem->cc.cbot) {
  721.     alloc_trace(":-o ", imem, cname, pstype, size, ptr);
  722.     gs_alloc_fill(ptr, gs_alloc_fill_free, size);
  723.     imem->cc.cbot = (byte *) pp;
  724.     /* IFF this object is adjacent to (or below) the byte after the
  725.      * highest free object, do the consolidation within this chunk. */
  726.     if ((byte *)pp <= imem->cc.int_freed_top) {
  727.         consolidate_chunk_free(&(imem->cc), imem);
  728.     }
  729.     return;
  730.     }
  731.     if (pp->o_alone) {
  732.         /*
  733.          * We gave this object its own chunk.  Free the entire chunk,
  734.          * unless it belongs to an older save level, in which case
  735.          * we mustn't overwrite it.
  736.          */
  737.     chunk_locator_t cl;
  738.  
  739. #ifdef DEBUG
  740.     {
  741.         chunk_locator_t cld;
  742.  
  743.         cld.memory = imem;
  744.         cld.cp = 0;
  745.         if (gs_debug_c('a'))
  746.         alloc_trace(
  747.                 (chunk_locate_ptr(ptr, &cld) ? ":-oL" : ":-o~"),
  748.                    imem, cname, pstype, size, ptr);
  749.     }
  750. #endif
  751.     cl.memory = imem;
  752.     cl.cp = 0;
  753.     if (chunk_locate_ptr(ptr, &cl)) {
  754.         if (!imem->is_controlled)
  755.         alloc_free_chunk(cl.cp, imem);
  756.         return;
  757.     }
  758.     /* Don't overwrite even if gs_alloc_debug is set. */
  759.     }
  760.     if (rounded_size >= sizeof(obj_header_t *)) {
  761.     /*
  762.      * Put the object on a freelist, unless it belongs to
  763.      * an older save level, in which case we mustn't
  764.      * overwrite it.
  765.      */
  766.     imem->cfreed.memory = imem;
  767.     if (chunk_locate(ptr, &imem->cfreed)) {
  768.         obj_header_t **pfl;
  769.  
  770.         if (size > max_freelist_size) {
  771.         pfl = &imem->freelists[LARGE_FREELIST_INDEX];
  772.         if (rounded_size > imem->largest_free_size)
  773.             imem->largest_free_size = rounded_size;
  774.         } else {
  775.         pfl = &imem->freelists[(size + obj_align_mask) >>
  776.                       log2_obj_align_mod];
  777.         }
  778.         /* keep track of highest object on a freelist */
  779.         if ((byte *)pp >= imem->cc.int_freed_top)
  780.         imem->cc.int_freed_top = (byte *)ptr + rounded_size;
  781.         pp->o_type = &st_free;    /* don't confuse GC */
  782.         gs_alloc_fill(ptr, gs_alloc_fill_free, size);
  783.         *(obj_header_t **) ptr = *pfl;
  784.         *pfl = (obj_header_t *) ptr;
  785.         alloc_trace((size > max_freelist_size ? ":-oF" : ":-of"),
  786.             imem, cname, pstype, size, ptr);
  787.         return;
  788.     }
  789.     /* Don't overwrite even if gs_alloc_debug is set. */
  790.     } else {
  791.     pp->o_type = &st_free;    /* don't confuse GC */
  792.     gs_alloc_fill(ptr, gs_alloc_fill_free, size);
  793.     }
  794.     alloc_trace(":-o#", imem, cname, pstype, size, ptr);
  795.     imem->lost.objects += obj_size_round(size);
  796. }
  797. private byte *
  798. i_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname)
  799. {
  800.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  801.     byte *str;
  802.  
  803. top:if (imem->cc.ctop - imem->cc.cbot > nbytes) {
  804.     if_debug4('A', "[a%d:+> ]%s(%u) = 0x%lx\n",
  805.           alloc_trace_space(imem), client_name_string(cname), nbytes,
  806.           (ulong) (imem->cc.ctop - nbytes));
  807.     str = imem->cc.ctop -= nbytes;
  808.     gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
  809.     return str;
  810.     }
  811.     if (nbytes > string_space_quanta(max_uint - sizeof(chunk_head_t)) *
  812.     string_data_quantum
  813.     ) {            /* Can't represent the size in a uint! */
  814.     return 0;
  815.     }
  816.     if (nbytes >= imem->large_size) {    /* Give it a chunk all its own. */
  817.     return i_alloc_string_immovable(mem, nbytes, cname);
  818.     } else {            /* Add another chunk. */
  819.     chunk_t *cp =
  820.         alloc_acquire_chunk(imem, (ulong) imem->chunk_size, true, "chunk");
  821.  
  822.     if (cp == 0)
  823.         return 0;
  824.     alloc_close_chunk(imem);
  825.     imem->pcc = cp;
  826.     imem->cc = *imem->pcc;
  827.     gs_alloc_fill(imem->cc.cbase, gs_alloc_fill_free,
  828.               imem->cc.climit - imem->cc.cbase);
  829.     goto top;
  830.     }
  831. }
  832. private byte *
  833. i_alloc_string_immovable(gs_memory_t * mem, uint nbytes, client_name_t cname)
  834. {
  835.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  836.     byte *str;
  837.     /* Give it a chunk all its own. */
  838.     uint asize = string_chunk_space(nbytes) + sizeof(chunk_head_t);
  839.     chunk_t *cp = alloc_acquire_chunk(imem, (ulong) asize, true,
  840.                       "large string chunk");
  841.  
  842.     if (cp == 0)
  843.     return 0;
  844.     str = cp->ctop = cp->climit - nbytes;
  845.     if_debug4('a', "[a%d|+>L]%s(%u) = 0x%lx\n",
  846.           alloc_trace_space(imem), client_name_string(cname), nbytes,
  847.           (ulong) str);
  848.     gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
  849.     return str;
  850. }
  851. private byte *
  852. i_resize_string(gs_memory_t * mem, byte * data, uint old_num, uint new_num,
  853.         client_name_t cname)
  854. {
  855.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  856.     byte *ptr;
  857.  
  858.     if (old_num == new_num)    /* same size returns the same string */
  859.         return data;
  860.     if (data == imem->cc.ctop &&    /* bottom-most string */
  861.     (new_num < old_num ||
  862.      imem->cc.ctop - imem->cc.cbot > new_num - old_num)
  863.     ) {            /* Resize in place. */
  864.     ptr = data + old_num - new_num;
  865.     if_debug6('A', "[a%d:%c> ]%s(%u->%u) 0x%lx\n",
  866.           alloc_trace_space(imem),
  867.           (new_num > old_num ? '>' : '<'),
  868.           client_name_string(cname), old_num, new_num,
  869.           (ulong) ptr);
  870.     imem->cc.ctop = ptr;
  871.     memmove(ptr, data, min(old_num, new_num));
  872. #ifdef DEBUG
  873.     if (new_num > old_num)
  874.         gs_alloc_fill(ptr + old_num, gs_alloc_fill_alloc,
  875.               new_num - old_num);
  876.     else
  877.         gs_alloc_fill(data, gs_alloc_fill_free, old_num - new_num);
  878. #endif
  879.     } else
  880.     if (new_num < old_num) {
  881.         /* trim the string and create a free space hole */
  882.         ptr = data;
  883.         imem->lost.strings += old_num - new_num;
  884.         gs_alloc_fill(data + new_num, gs_alloc_fill_free,
  885.               old_num - new_num);
  886.         if_debug5('A', "[a%d:<> ]%s(%u->%u) 0x%lx\n",
  887.               alloc_trace_space(imem), client_name_string(cname),
  888.               old_num, new_num, (ulong)ptr);
  889.         } else {            /* Punt. */
  890.         ptr = gs_alloc_string(mem, new_num, cname);
  891.         if (ptr == 0)
  892.         return 0;
  893.         memcpy(ptr, data, min(old_num, new_num));
  894.         gs_free_string(mem, data, old_num, cname);
  895.     }
  896.     return ptr;
  897. }
  898.  
  899. private void
  900. i_free_string(gs_memory_t * mem, byte * data, uint nbytes,
  901.           client_name_t cname)
  902. {
  903.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  904.     if (data == imem->cc.ctop) {
  905.     if_debug4('A', "[a%d:-> ]%s(%u) 0x%lx\n",
  906.           alloc_trace_space(imem), client_name_string(cname), nbytes,
  907.           (ulong) data);
  908.     imem->cc.ctop += nbytes;
  909.     } else {
  910.     if_debug4('A', "[a%d:->#]%s(%u) 0x%lx\n",
  911.           alloc_trace_space(imem), client_name_string(cname), nbytes,
  912.           (ulong) data);
  913.     imem->lost.strings += nbytes;
  914.     }
  915.     gs_alloc_fill(data, gs_alloc_fill_free, nbytes);
  916. }
  917.  
  918. private gs_memory_t *
  919. i_stable(gs_memory_t *mem)
  920. {
  921.     return mem->stable_memory;
  922. }
  923.  
  924. private void
  925. i_status(gs_memory_t * mem, gs_memory_status_t * pstat)
  926. {
  927.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  928.     ulong unused = imem->lost.refs + imem->lost.strings;
  929.     ulong inner = 0;
  930.  
  931.     alloc_close_chunk(imem);
  932.     /* Add up unallocated space within each chunk. */
  933.     /* Also keep track of space allocated to inner chunks, */
  934.     /* which are included in previous_status.allocated. */
  935.     {
  936.     const chunk_t *cp = imem->cfirst;
  937.  
  938.     while (cp != 0) {
  939.         unused += cp->ctop - cp->cbot;
  940.         if (cp->outer)
  941.         inner += cp->cend - (byte *) cp->chead;
  942.         cp = cp->cnext;
  943.     }
  944.     }
  945.     unused += compute_free_objects(imem);
  946.     pstat->used = imem->allocated + inner - unused +
  947.     imem->previous_status.used;
  948.     pstat->allocated = imem->allocated +
  949.     imem->previous_status.allocated;
  950. }
  951.  
  952. private void
  953. i_enable_free(gs_memory_t * mem, bool enable)
  954. {
  955.     if (enable)
  956.     mem->procs.free_object = i_free_object,
  957.         mem->procs.free_string = i_free_string;
  958.     else
  959.     mem->procs.free_object = gs_ignore_free_object,
  960.         mem->procs.free_string = gs_ignore_free_string;
  961. }
  962.  
  963. /* ------ Internal procedures ------ */
  964.  
  965. /* Compute the amount of free object space by scanning free lists. */
  966. private ulong
  967. compute_free_objects(gs_ref_memory_t * mem)
  968. {
  969.     ulong unused = mem->lost.objects;
  970.     int i;
  971.  
  972.     /* Add up space on free lists. */
  973.     for (i = 0; i < num_freelists; i++) {
  974.     const obj_header_t *pfree;
  975.  
  976.     for (pfree = mem->freelists[i]; pfree != 0;
  977.          pfree = *(const obj_header_t * const *)pfree
  978.         )
  979.         unused += obj_align_round(pfree[-1].o_size);
  980.     }
  981.     return unused;
  982. }
  983.     
  984. /* Allocate an object from the large-block freelist. */
  985. private obj_header_t *    /* rets obj if allocated, else 0 */
  986. large_freelist_alloc(gs_ref_memory_t *mem, uint size)
  987. {
  988.     /* Scan large object freelist. We'll grab an object up to 1/8 bigger */
  989.     /* right away, else use best fit of entire scan. */
  990.     uint aligned_size = obj_align_round(size);
  991.     uint aligned_min_size = aligned_size + sizeof(obj_header_t);
  992.     uint aligned_max_size =
  993.     aligned_min_size + obj_align_round(aligned_min_size / 8);
  994.     obj_header_t *best_fit = 0;
  995.     obj_header_t **best_fit_prev;
  996.     uint best_fit_size = max_uint;
  997.     obj_header_t *pfree;
  998.     obj_header_t **ppfprev = &mem->freelists[LARGE_FREELIST_INDEX];
  999.     uint largest_size = 0;
  1000.  
  1001.     if (aligned_size > mem->largest_free_size)
  1002.     return 0;        /* definitely no block large enough */
  1003.  
  1004.     while ((pfree = *ppfprev) != 0) {
  1005.     uint free_size = obj_align_round(pfree[-1].o_size);
  1006.  
  1007.         if (free_size == aligned_size ||
  1008.         (free_size >= aligned_min_size && free_size < best_fit_size)
  1009.         ) {
  1010.         best_fit = pfree;
  1011.         best_fit_prev = ppfprev;
  1012.         best_fit_size = pfree[-1].o_size;
  1013.         if (best_fit_size <= aligned_max_size)
  1014.         break;    /* good enough fit to spare scan of entire list */
  1015.     }
  1016.     ppfprev = (obj_header_t **) pfree;
  1017.     if (free_size > largest_size)
  1018.         largest_size = free_size;
  1019.     }
  1020.     if (best_fit == 0) {
  1021.     /*
  1022.      * No single free chunk is large enough, but since we scanned the
  1023.      * entire list, we now have an accurate updated value for
  1024.      * largest_free_size.
  1025.      */
  1026.     mem->largest_free_size = largest_size;
  1027.     return 0;
  1028.     }
  1029.  
  1030.     /* Remove from freelist & return excess memory to free */
  1031.     *best_fit_prev = *(obj_header_t **)best_fit;
  1032.     trim_obj(mem, best_fit, aligned_size, (chunk_t *)0);
  1033.  
  1034.     /* Pre-init block header; o_alone & o_type are already init'd */
  1035.     best_fit[-1].o_size = size;
  1036.  
  1037.     return best_fit;
  1038. }
  1039.  
  1040. /* Allocate an object.  This handles all but the fastest, simplest case. */
  1041. private obj_header_t *
  1042. alloc_obj(gs_ref_memory_t *mem, ulong lsize, gs_memory_type_ptr_t pstype,
  1043.       alloc_flags_t flags, client_name_t cname)
  1044. {
  1045.     obj_header_t *ptr;
  1046.  
  1047.     if (lsize >= mem->large_size || (flags & ALLOC_IMMOVABLE)) {
  1048.     /*
  1049.      * Give the object a chunk all its own.  Note that this case does
  1050.      * not occur if is_controlled is true.
  1051.      */
  1052.     ulong asize =
  1053.         ((lsize + obj_align_mask) & -obj_align_mod) +
  1054.         sizeof(obj_header_t);
  1055.     chunk_t *cp =
  1056.         alloc_acquire_chunk(mem, asize + sizeof(chunk_head_t), false,
  1057.                 "large object chunk");
  1058.  
  1059.     if (
  1060. #if arch_sizeof_long > arch_sizeof_int
  1061.         asize > max_uint
  1062. #else
  1063.         asize < lsize
  1064. #endif
  1065.         )
  1066.         return 0;
  1067.     if (cp == 0)
  1068.         return 0;
  1069.     ptr = (obj_header_t *) cp->cbot;
  1070.     cp->cbot += asize;
  1071.     ptr->o_alone = 1;
  1072.     ptr->o_size = lsize;
  1073.     } else if (lsize > max_freelist_size && (flags & ALLOC_DIRECT) &&
  1074.           (ptr = large_freelist_alloc(mem, lsize)) != 0) {
  1075.     /* We hadn't checked the large block freelist yet. */
  1076.     --ptr;            /* must point to header */
  1077.     goto done;
  1078.     } else {
  1079.     uint asize = obj_size_round((uint) lsize);
  1080.     bool consolidate = mem->is_controlled;
  1081.     bool allocate_success = true;
  1082.  
  1083.     while (mem->cc.ctop -
  1084.            (byte *) (ptr = (obj_header_t *) mem->cc.cbot)
  1085.            <= asize + sizeof(obj_header_t)) {
  1086.         if (consolidate) {
  1087.         /* Try consolidating free space. */
  1088.         gs_consolidate_free((gs_memory_t *)mem);
  1089.         consolidate = false;
  1090.         continue;
  1091.         } else {
  1092.         /* Add another chunk. */
  1093.         chunk_t *cp =
  1094.             alloc_add_chunk(mem, (ulong)mem->chunk_size, "chunk");
  1095.  
  1096.         if (cp == 0) {
  1097.             allocate_success = false;
  1098.             break;
  1099.         }
  1100.         }
  1101.     }
  1102.     /*
  1103.      * If no success, try to scavenge from low free memory. This is only
  1104.      * enabled for controlled memory (currently only async renderer)
  1105.      * because it's too much work to prevent it from examining outer
  1106.      * save levels in the general case.
  1107.      */
  1108.     if (allocate_success)
  1109.         mem->cc.cbot = (byte *) ptr + asize;
  1110.     else if (!mem->is_controlled ||
  1111.          (ptr = scavenge_low_free(mem, (uint)lsize)) == 0)
  1112.         return 0;    /* allocation failed */
  1113.     ptr->o_alone = 0;
  1114.     ptr->o_size = (uint) lsize;
  1115.     }
  1116. done:
  1117.     ptr->o_type = pstype;
  1118.     ptr++;
  1119.     gs_alloc_fill(ptr, gs_alloc_fill_alloc, lsize);
  1120.     return ptr;
  1121. }
  1122.  
  1123. /*
  1124.  * Consolidate free objects contiguous to free space at cbot onto the cbot
  1125.  * area. Also keep track of end of highest internal free object
  1126.  * (int_freed_top).
  1127.  */
  1128. private void
  1129. consolidate_chunk_free(chunk_t *cp, gs_ref_memory_t *mem)
  1130. {
  1131.     obj_header_t *begin_free = 0;
  1132.  
  1133.     cp->int_freed_top = cp->cbase;    /* below all objects in chunk */
  1134.     SCAN_CHUNK_OBJECTS(cp)
  1135.     DO_ALL
  1136.     if (pre->o_type == &st_free) {
  1137.         if (begin_free == 0)
  1138.         begin_free = pre;
  1139.     } else {
  1140.         if (begin_free)
  1141.         cp->int_freed_top = (byte *)pre; /* first byte following internal free */
  1142.         begin_free = 0;
  1143.         }
  1144.     END_OBJECTS_SCAN
  1145.     if (begin_free) {
  1146.     /* We found free objects at the top of the object area. */
  1147.     /* Remove the free objects from the freelists. */
  1148.     remove_range_from_freelist(mem, begin_free, cp->cbot);
  1149.     if_debug4('a', "[a]resetting chunk 0x%lx cbot from 0x%lx to 0x%lx (%lu free)\n",
  1150.           (ulong) cp, (ulong) cp->cbot, (ulong) begin_free,
  1151.           (ulong) ((byte *) cp->cbot - (byte *) begin_free));
  1152.     cp->cbot = (byte *) begin_free;
  1153.     }
  1154. }
  1155.  
  1156. /* Consolidate free objects. */
  1157. void
  1158. ialloc_consolidate_free(gs_ref_memory_t *mem)
  1159. {
  1160.     chunk_t *cp;
  1161.     chunk_t *cprev;
  1162.  
  1163.     alloc_close_chunk(mem);
  1164.  
  1165.     /* Visit chunks in reverse order to encourage LIFO behavior. */
  1166.     for (cp = mem->clast; cp != 0; cp = cprev) {
  1167.     cprev = cp->cprev;
  1168.     consolidate_chunk_free(cp, mem);
  1169.     if (cp->cbot == cp->cbase && cp->ctop == cp->climit) {
  1170.         /* The entire chunk is free. */
  1171.         chunk_t *cnext = cp->cnext;
  1172.  
  1173.         if (!mem->is_controlled) {
  1174.         alloc_free_chunk(cp, mem);
  1175.         if (mem->pcc == cp)
  1176.             mem->pcc =
  1177.             (cnext == 0 ? cprev : cprev == 0 ? cnext :
  1178.              cprev->cbot - cprev->ctop >
  1179.              cnext->cbot - cnext->ctop ? cprev :
  1180.              cnext);
  1181.         }
  1182.     }
  1183.     }
  1184.     alloc_open_chunk(mem);
  1185. }
  1186. private void
  1187. i_consolidate_free(gs_memory_t *mem)
  1188. {
  1189.     ialloc_consolidate_free((gs_ref_memory_t *)mem);
  1190. }
  1191.  
  1192. /* try to free-up given amount of space from freespace below chunk base */
  1193. private obj_header_t *    /* returns uninitialized object hdr, NULL if none found */
  1194. scavenge_low_free(gs_ref_memory_t *mem, unsigned request_size)
  1195. {
  1196.     /* find 1st range of memory that can be glued back together to fill request */
  1197.     obj_header_t *found_pre = 0;
  1198.  
  1199.     /* Visit chunks in forward order */
  1200.     obj_header_t *begin_free = 0;
  1201.     uint found_free;
  1202.     uint request_size_rounded = obj_size_round(request_size);
  1203.     uint need_free = request_size_rounded + sizeof(obj_header_t);    /* room for GC's dummy hdr */
  1204.     chunk_t *cp;
  1205.  
  1206.     for (cp = mem->cfirst; cp != 0; cp = cp->cnext) {
  1207.     begin_free = 0;
  1208.     found_free = 0;
  1209.     SCAN_CHUNK_OBJECTS(cp)
  1210.     DO_ALL
  1211.         if (pre->o_type == &st_free) {
  1212.             if (begin_free == 0) {
  1213.                 found_free = 0;
  1214.                 begin_free = pre;
  1215.             }
  1216.             found_free += pre_obj_rounded_size(pre);
  1217.             if (begin_free != 0 && found_free >= need_free)
  1218.                 break;
  1219.         } else
  1220.             begin_free = 0;
  1221.     END_OBJECTS_SCAN_INCOMPLETE
  1222.  
  1223.     /* Found sufficient range of empty memory */
  1224.     if (begin_free != 0 && found_free >= need_free) {
  1225.  
  1226.         /* Fish found pieces out of various freelists */
  1227.         remove_range_from_freelist(mem, (char*)begin_free,
  1228.                        (char*)begin_free + found_free);
  1229.  
  1230.         /* Prepare found object */
  1231.         found_pre = begin_free;
  1232.         found_pre->o_type = &st_free;  /* don't confuse GC if gets lost */
  1233.         found_pre->o_size = found_free - sizeof(obj_header_t);
  1234.  
  1235.         /* Chop off excess tail piece & toss it back into free pool */
  1236.         trim_obj(mem, found_pre + 1, request_size, cp);
  1237.          }
  1238.     }
  1239.     return found_pre;
  1240. }
  1241.  
  1242. /* Remove range of memory from a mem's freelists */
  1243. private void
  1244. remove_range_from_freelist(gs_ref_memory_t *mem, void* bottom, void* top)
  1245. {
  1246.     int num_free[num_freelists];
  1247.     int smallest = num_freelists, largest = -1;
  1248.     const obj_header_t *cur;
  1249.     uint size;
  1250.     int i;
  1251.     uint removed = 0;
  1252.  
  1253.     /*
  1254.      * Scan from bottom to top, a range containing only free objects,
  1255.      * counting the number of objects of each size.
  1256.      */
  1257.  
  1258.     for (cur = bottom; cur != top;
  1259.      cur = (const obj_header_t *)
  1260.          ((const byte *)cur + obj_size_round(size))
  1261.     ) {
  1262.     size = cur->o_size;
  1263.     i = (size > max_freelist_size ? LARGE_FREELIST_INDEX :
  1264.          (size + obj_align_mask) >> log2_obj_align_mod);
  1265.     if (i < smallest) {
  1266.         /*
  1267.          * 0-length free blocks aren't kept on any list, because
  1268.          * they don't have room for a pointer.
  1269.          */
  1270.         if (i == 0)
  1271.         continue;
  1272.         if (smallest < num_freelists)
  1273.         memset(&num_free[i], 0, (smallest - i) * sizeof(int));
  1274.         else
  1275.         num_free[i] = 0;
  1276.         smallest = i;
  1277.     }
  1278.     if (i > largest) {
  1279.         if (largest >= 0)
  1280.         memset(&num_free[largest + 1], 0, (i - largest) * sizeof(int));
  1281.         largest = i;
  1282.     }
  1283.     num_free[i]++;
  1284.     }
  1285.  
  1286.     /*
  1287.      * Remove free objects from the freelists, adjusting lost.objects by
  1288.      * subtracting the size of the region being processed minus the amount
  1289.      * of space reclaimed.
  1290.      */
  1291.  
  1292.     for (i = smallest; i <= largest; i++) {
  1293.     int count = num_free[i];
  1294.         obj_header_t *pfree;
  1295.     obj_header_t **ppfprev;
  1296.  
  1297.     if (!count)
  1298.         continue;
  1299.     ppfprev = &mem->freelists[i];
  1300.     for (;;) {
  1301.         pfree = *ppfprev;
  1302.         if (PTR_GE(pfree, bottom) && PTR_LT(pfree, top)) {
  1303.         /* We're removing an object. */
  1304.         *ppfprev = *(obj_header_t **) pfree;
  1305.         removed += obj_align_round(pfree[-1].o_size);
  1306.         if (!--count)
  1307.             break;
  1308.         } else
  1309.         ppfprev = (obj_header_t **) pfree;
  1310.     }
  1311.     }
  1312.     mem->lost.objects -= (char*)top - (char*)bottom - removed;
  1313. }
  1314.  
  1315. /* Trim a memory object down to a given size */
  1316. private void
  1317. trim_obj(gs_ref_memory_t *mem, obj_header_t *obj, uint size, chunk_t *cp)
  1318. /* Obj must have rounded size == req'd size, or have enough room for */
  1319. /* trailing dummy obj_header */
  1320. {
  1321.     uint rounded_size = obj_align_round(size);
  1322.     obj_header_t *pre_obj = obj - 1;
  1323.     obj_header_t *excess_pre = (obj_header_t*)((char*)obj + rounded_size);
  1324.     uint old_rounded_size = obj_align_round(pre_obj->o_size);
  1325.     uint excess_size = old_rounded_size - rounded_size - sizeof(obj_header_t);
  1326.  
  1327.     /* trim object's size to desired */
  1328.     if (old_rounded_size == rounded_size)
  1329.     return;    /* nothing to do here */
  1330.     pre_obj->o_size = size;
  1331.     /*
  1332.      * If the object is alone in its chunk, move cbot to point to the end
  1333.      * of the object.
  1334.      */
  1335.     if (pre_obj->o_alone) {
  1336.     if (!cp) {
  1337.         mem->cfreed.memory = mem;
  1338.         if (chunk_locate(obj, &mem->cfreed)) {
  1339.         cp = mem->cfreed.cp;
  1340.         }
  1341.     }
  1342.     if (cp) {
  1343. #ifdef DEBUG
  1344.         if (cp->cbot != (byte *)obj + old_rounded_size) {
  1345.         lprintf3("resizing 0x%lx, old size %u, new size %u, cbot wrong!\n",
  1346.              (ulong)obj, old_rounded_size, size);
  1347.         /* gs_abort */
  1348.         } else
  1349. #endif
  1350.         {
  1351.             cp->cbot = (byte *)excess_pre;
  1352.             return;
  1353.         }
  1354.     }
  1355.     /*
  1356.      * Something very weird is going on.  This probably shouldn't
  1357.      * ever happen, but if it does....
  1358.      */
  1359.     pre_obj->o_alone = 0;
  1360.     }
  1361.     /* make excess into free obj */
  1362.     excess_pre->o_type = &st_free;  /* don't confuse GC */
  1363.     excess_pre->o_size = excess_size;
  1364.     excess_pre->o_alone = 0;
  1365.     if (excess_size >= obj_align_mod) {
  1366.     /* Put excess object on a freelist */
  1367.     obj_header_t **pfl;
  1368.  
  1369.     if ((byte *)excess_pre >= mem->cc.int_freed_top)
  1370.         mem->cc.int_freed_top = (byte *)excess_pre + excess_size;
  1371.     if (excess_size <= max_freelist_size)
  1372.         pfl = &mem->freelists[(excess_size + obj_align_mask) >>
  1373.                  log2_obj_align_mod];
  1374.     else {
  1375.         uint rounded_size = obj_align_round(excess_size);
  1376.  
  1377.         pfl = &mem->freelists[LARGE_FREELIST_INDEX];
  1378.         if (rounded_size > mem->largest_free_size)
  1379.         mem->largest_free_size = rounded_size;
  1380.     }
  1381.     *(obj_header_t **) (excess_pre + 1) = *pfl;
  1382.     *pfl = excess_pre + 1;
  1383.     mem->cfreed.memory = mem;
  1384.     } else {
  1385.     /* excess piece will be "lost" memory */
  1386.     mem->lost.objects += excess_size + sizeof(obj_header_t);
  1387.     }
  1388. }    
  1389.  
  1390. /* ================ Roots ================ */
  1391.  
  1392. /* Register a root. */
  1393. private int
  1394. i_register_root(gs_memory_t * mem, gs_gc_root_t * rp, gs_ptr_type_t ptype,
  1395.         void **up, client_name_t cname)
  1396. {
  1397.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  1398.  
  1399.     if (rp == NULL) {
  1400.     rp = gs_raw_alloc_struct_immovable(imem->parent, &st_gc_root_t,
  1401.                        "i_register_root");
  1402.     if (rp == 0)
  1403.         return_error(gs_error_VMerror);
  1404.     rp->free_on_unregister = true;
  1405.     } else
  1406.     rp->free_on_unregister = false;
  1407.     if_debug3('8', "[8]register root(%s) 0x%lx -> 0x%lx\n",
  1408.           client_name_string(cname), (ulong)rp, (ulong)up);
  1409.     rp->ptype = ptype;
  1410.     rp->p = up;
  1411.     rp->next = imem->roots;
  1412.     imem->roots = rp;
  1413.     return 0;
  1414. }
  1415.  
  1416. /* Unregister a root. */
  1417. private void
  1418. i_unregister_root(gs_memory_t * mem, gs_gc_root_t * rp, client_name_t cname)
  1419. {
  1420.     gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
  1421.     gs_gc_root_t **rpp = &imem->roots;
  1422.  
  1423.     if_debug2('8', "[8]unregister root(%s) 0x%lx\n",
  1424.           client_name_string(cname), (ulong) rp);
  1425.     while (*rpp != rp)
  1426.     rpp = &(*rpp)->next;
  1427.     *rpp = (*rpp)->next;
  1428.     if (rp->free_on_unregister)
  1429.     gs_free_object(imem->parent, rp, "i_unregister_root");
  1430. }
  1431.  
  1432. /* ================ Chunks ================ */
  1433.  
  1434. public_st_chunk();
  1435.  
  1436. /* Insert a chunk in the chain.  This is exported for the GC and for */
  1437. /* the forget_save operation. */
  1438. void
  1439. alloc_link_chunk(chunk_t * cp, gs_ref_memory_t * imem)
  1440. {
  1441.     byte *cdata = cp->cbase;
  1442.     chunk_t *icp;
  1443.     chunk_t *prev;
  1444.  
  1445.     /*
  1446.      * Allocators tend to allocate in either ascending or descending
  1447.      * address order.  The loop will handle the latter well; check for
  1448.      * the former first.
  1449.      */
  1450.     if (imem->clast && PTR_GE(cdata, imem->clast->ctop))
  1451.     icp = 0;
  1452.     else
  1453.     for (icp = imem->cfirst; icp != 0 && PTR_GE(cdata, icp->ctop);
  1454.          icp = icp->cnext
  1455.         );
  1456.     cp->cnext = icp;
  1457.     if (icp == 0) {        /* add at end of chain */
  1458.     prev = imem->clast;
  1459.     imem->clast = cp;
  1460.     } else {            /* insert before icp */
  1461.     prev = icp->cprev;
  1462.     icp->cprev = cp;
  1463.     }
  1464.     cp->cprev = prev;
  1465.     if (prev == 0)
  1466.     imem->cfirst = cp;
  1467.     else
  1468.     prev->cnext = cp;
  1469.     if (imem->pcc != 0) {
  1470.     imem->cc.cnext = imem->pcc->cnext;
  1471.     imem->cc.cprev = imem->pcc->cprev;
  1472.     }
  1473. }
  1474.  
  1475. /* Add a chunk for ordinary allocation. */
  1476. private chunk_t *
  1477. alloc_add_chunk(gs_ref_memory_t * mem, ulong csize, client_name_t cname)
  1478. {
  1479.     chunk_t *cp = alloc_acquire_chunk(mem, csize, true, cname);
  1480.  
  1481.     if (cp) {
  1482.     alloc_close_chunk(mem);
  1483.     mem->pcc = cp;
  1484.     mem->cc = *mem->pcc;
  1485.     gs_alloc_fill(mem->cc.cbase, gs_alloc_fill_free,
  1486.               mem->cc.climit - mem->cc.cbase);
  1487.     }
  1488.     return cp;
  1489. }
  1490.  
  1491. /* Acquire a chunk.  If we would exceed MaxLocalVM (if relevant), */
  1492. /* or if we would exceed the VMThreshold and psignal is NULL, */
  1493. /* return 0; if we would exceed the VMThreshold but psignal is valid, */
  1494. /* just set the signal and return successfully. */
  1495. private chunk_t *
  1496. alloc_acquire_chunk(gs_ref_memory_t * mem, ulong csize, bool has_strings,
  1497.             client_name_t cname)
  1498. {
  1499.     gs_raw_memory_t *parent = mem->parent;
  1500.     chunk_t *cp;
  1501.     byte *cdata;
  1502.  
  1503. #if arch_sizeof_long > arch_sizeof_int
  1504.     /* If csize is larger than max_uint, punt. */
  1505.     if (csize != (uint) csize)
  1506.     return 0;
  1507. #endif
  1508.     cp = gs_raw_alloc_struct_immovable(parent, &st_chunk, cname);
  1509.     if ((ulong) (mem->allocated + mem->inherited) >= mem->limit) {
  1510.     mem->gc_status.requested += csize;
  1511.     if (mem->limit >= mem->gc_status.max_vm ||
  1512.         mem->gc_status.psignal == 0
  1513.         )
  1514.         return 0;
  1515.     if_debug4('0', "[0]signaling space=%d, allocated=%ld, limit=%ld, requested=%ld\n",
  1516.           mem->space, (long)mem->allocated,
  1517.           (long)mem->limit, (long)mem->gc_status.requested);
  1518.     *mem->gc_status.psignal = mem->gc_status.signal_value;
  1519.     }
  1520.     cdata = gs_alloc_bytes_immovable(parent, csize, cname);
  1521.     if (cp == 0 || cdata == 0) {
  1522.     gs_free_object(parent, cdata, cname);
  1523.     gs_free_object(parent, cp, cname);
  1524.     mem->gc_status.requested = csize;
  1525.     return 0;
  1526.     }
  1527.     alloc_init_chunk(cp, cdata, cdata + csize, has_strings, (chunk_t *) 0);
  1528.     alloc_link_chunk(cp, mem);
  1529.     mem->allocated += st_chunk.ssize + csize;
  1530.     return cp;
  1531. }
  1532.  
  1533. /* Initialize the pointers in a chunk.  This is exported for save/restore. */
  1534. /* The bottom pointer must be aligned, but the top pointer need not */
  1535. /* be aligned. */
  1536. void
  1537. alloc_init_chunk(chunk_t * cp, byte * bot, byte * top, bool has_strings,
  1538.          chunk_t * outer)
  1539. {
  1540.     byte *cdata = bot;
  1541.  
  1542.     if (outer != 0)
  1543.     outer->inner_count++;
  1544.     cp->chead = (chunk_head_t *) cdata;
  1545.     cdata += sizeof(chunk_head_t);
  1546.     cp->cbot = cp->cbase = cp->int_freed_top = cdata;
  1547.     cp->cend = top;
  1548.     cp->rcur = 0;
  1549.     cp->rtop = 0;
  1550.     cp->outer = outer;
  1551.     cp->inner_count = 0;
  1552.     cp->has_refs = false;
  1553.     cp->sbase = cdata;
  1554.     if (has_strings && top - cdata >= string_space_quantum + sizeof(long) - 1) {
  1555.     /*
  1556.      * We allocate a large enough string marking and reloc table
  1557.      * to cover the entire chunk.
  1558.      */
  1559.     uint nquanta = string_space_quanta(top - cdata);
  1560.  
  1561.     cp->climit = cdata + nquanta * string_data_quantum;
  1562.     cp->smark = cp->climit;
  1563.     cp->smark_size = string_quanta_mark_size(nquanta);
  1564.     cp->sreloc =
  1565.         (string_reloc_offset *) (cp->smark + cp->smark_size);
  1566.     cp->sfree1 = (uint *) cp->sreloc;
  1567.     } else {
  1568.     /* No strings, don't need the string GC tables. */
  1569.     cp->climit = cp->cend;
  1570.     cp->sfree1 = 0;
  1571.     cp->smark = 0;
  1572.     cp->smark_size = 0;
  1573.     cp->sreloc = 0;
  1574.     }
  1575.     cp->ctop = cp->climit;
  1576.     alloc_init_free_strings(cp);
  1577. }
  1578.  
  1579. /* Initialize the string freelists in a chunk. */
  1580. void
  1581. alloc_init_free_strings(chunk_t * cp)
  1582. {
  1583.     if (cp->sfree1)
  1584.     memset(cp->sfree1, 0, STRING_FREELIST_SPACE(cp));
  1585.     cp->sfree = 0;
  1586. }
  1587.  
  1588. /* Close up the current chunk. */
  1589. /* This is exported for save/restore and the GC. */
  1590. void
  1591. alloc_close_chunk(gs_ref_memory_t * mem)
  1592. {
  1593.     if (mem->pcc != 0) {
  1594.     *mem->pcc = mem->cc;
  1595. #ifdef DEBUG
  1596.     if (gs_debug_c('a')) {
  1597.         dlprintf1("[a%d]", alloc_trace_space(mem));
  1598.         dprintf_chunk("closing chunk", mem->pcc);
  1599.     }
  1600. #endif
  1601.     }
  1602. }
  1603.  
  1604. /* Reopen the current chunk after a GC or restore. */
  1605. void
  1606. alloc_open_chunk(gs_ref_memory_t * mem)
  1607. {
  1608.     if (mem->pcc != 0) {
  1609.     mem->cc = *mem->pcc;
  1610. #ifdef DEBUG
  1611.     if (gs_debug_c('a')) {
  1612.         dlprintf1("[a%d]", alloc_trace_space(mem));
  1613.         dprintf_chunk("opening chunk", mem->pcc);
  1614.     }
  1615. #endif
  1616.     }
  1617. }
  1618.  
  1619. /* Remove a chunk from the chain.  This is exported for the GC. */
  1620. void
  1621. alloc_unlink_chunk(chunk_t * cp, gs_ref_memory_t * mem)
  1622. {
  1623. #ifdef DEBUG
  1624.     if (gs_alloc_debug) {    /* Check to make sure this chunk belongs to this allocator. */
  1625.     const chunk_t *ap = mem->cfirst;
  1626.  
  1627.     while (ap != 0 && ap != cp)
  1628.         ap = ap->cnext;
  1629.     if (ap != cp) {
  1630.         lprintf2("unlink_chunk 0x%lx not owned by memory 0x%lx!\n",
  1631.              (ulong) cp, (ulong) mem);
  1632.         return;        /*gs_abort(); */
  1633.     }
  1634.     }
  1635. #endif
  1636.     if (cp->cprev == 0)
  1637.     mem->cfirst = cp->cnext;
  1638.     else
  1639.     cp->cprev->cnext = cp->cnext;
  1640.     if (cp->cnext == 0)
  1641.     mem->clast = cp->cprev;
  1642.     else
  1643.     cp->cnext->cprev = cp->cprev;
  1644.     if (mem->pcc != 0) {
  1645.     mem->cc.cnext = mem->pcc->cnext;
  1646.     mem->cc.cprev = mem->pcc->cprev;
  1647.     if (mem->pcc == cp) {
  1648.         mem->pcc = 0;
  1649.         mem->cc.cbot = mem->cc.ctop = 0;
  1650.     }
  1651.     }
  1652. }
  1653.  
  1654. /*
  1655.  * Free a chunk.  This is exported for the GC.  Since we eventually use
  1656.  * this to free the chunk containing the allocator itself, we must be
  1657.  * careful not to reference anything in the allocator after freeing the
  1658.  * chunk data.
  1659.  */
  1660. void
  1661. alloc_free_chunk(chunk_t * cp, gs_ref_memory_t * mem)
  1662. {
  1663.     gs_raw_memory_t *parent = mem->parent;
  1664.     byte *cdata = (byte *)cp->chead;
  1665.     ulong csize = (byte *)cp->cend - cdata;
  1666.  
  1667.     alloc_unlink_chunk(cp, mem);
  1668.     mem->allocated -= st_chunk.ssize;
  1669.     if (mem->cfreed.cp == cp)
  1670.     mem->cfreed.cp = 0;
  1671.     if (cp->outer == 0) {
  1672.     mem->allocated -= csize;
  1673.     gs_free_object(parent, cdata, "alloc_free_chunk(data)");
  1674.     } else {
  1675.     cp->outer->inner_count--;
  1676.     gs_alloc_fill(cdata, gs_alloc_fill_free, csize);
  1677.     }
  1678.     gs_free_object(parent, cp, "alloc_free_chunk(chunk struct)");
  1679. }
  1680.  
  1681. /* Find the chunk for a pointer. */
  1682. /* Note that this only searches the current save level. */
  1683. /* Since a given save level can't contain both a chunk and an inner chunk */
  1684. /* of that chunk, we can stop when is_within_chunk succeeds, and just test */
  1685. /* is_in_inner_chunk then. */
  1686. bool
  1687. chunk_locate_ptr(const void *ptr, chunk_locator_t * clp)
  1688. {
  1689.     register chunk_t *cp = clp->cp;
  1690.  
  1691.     if (cp == 0) {
  1692.     cp = clp->memory->cfirst;
  1693.     if (cp == 0)
  1694.         return false;
  1695.     /* ptr is in the last chunk often enough to be worth checking for. */
  1696.     if (PTR_GE(ptr, clp->memory->clast->cbase))
  1697.         cp = clp->memory->clast;
  1698.     }
  1699.     if (PTR_LT(ptr, cp->cbase)) {
  1700.     do {
  1701.         cp = cp->cprev;
  1702.         if (cp == 0)
  1703.         return false;
  1704.     }
  1705.     while (PTR_LT(ptr, cp->cbase));
  1706.     if (PTR_GE(ptr, cp->cend))
  1707.         return false;
  1708.     } else {
  1709.     while (PTR_GE(ptr, cp->cend)) {
  1710.         cp = cp->cnext;
  1711.         if (cp == 0)
  1712.         return false;
  1713.     }
  1714.     if (PTR_LT(ptr, cp->cbase))
  1715.         return false;
  1716.     }
  1717.     clp->cp = cp;
  1718.     return !ptr_is_in_inner_chunk(ptr, cp);
  1719. }
  1720.  
  1721. /* ------ Debugging ------ */
  1722.  
  1723. #ifdef DEBUG
  1724.  
  1725. #include "string_.h"
  1726.  
  1727. inline private bool
  1728. obj_in_control_region(const void *obot, const void *otop,
  1729.               const dump_control_t *pdc)
  1730. {
  1731.     return
  1732.     ((pdc->bottom == NULL || PTR_GT(otop, pdc->bottom)) &&
  1733.      (pdc->top == NULL || PTR_LT(obot, pdc->top)));
  1734. }
  1735.  
  1736. const dump_control_t dump_control_default =
  1737. {
  1738.     dump_do_default, NULL, NULL
  1739. };
  1740. const dump_control_t dump_control_all =
  1741. {
  1742.     dump_do_strings | dump_do_type_addresses | dump_do_pointers |
  1743.     dump_do_pointed_strings | dump_do_contents, NULL, NULL
  1744. };
  1745.  
  1746. /*
  1747.  * Internal procedure to dump a block of memory, in hex and optionally
  1748.  * also as characters.
  1749.  */
  1750. private void
  1751. debug_indent(int indent)
  1752. {
  1753.     int i;
  1754.  
  1755.     for (i = indent; i > 0; --i)
  1756.     dputc(' ');
  1757. }
  1758. private void
  1759. debug_dump_contents(const byte * bot, const byte * top, int indent,
  1760.             bool as_chars)
  1761. {
  1762.     const byte *block;
  1763.  
  1764. #define block_size 16
  1765.  
  1766.     if (bot >= top)
  1767.     return;
  1768.     for (block = bot - ((bot - (byte *) 0) & (block_size - 1));
  1769.      block < top; block += block_size
  1770.     ) {
  1771.     int i;
  1772.     char label[12];
  1773.  
  1774.     /* Check for repeated blocks. */
  1775.     if (block >= bot + block_size &&
  1776.         block <= top - (block_size * 2) &&
  1777.         !memcmp(block, block - block_size, block_size) &&
  1778.         !memcmp(block, block + block_size, block_size)
  1779.         ) {
  1780.         if (block < bot + block_size * 2 ||
  1781.         memcmp(block, block - block_size * 2, block_size)
  1782.         ) {
  1783.         debug_indent(indent);
  1784.         dputs("  ...\n");
  1785.         }
  1786.         continue;
  1787.     }
  1788.     sprintf(label, "0x%lx:", (ulong) block);
  1789.     debug_indent(indent);
  1790.     dputs(label);
  1791.     for (i = 0; i < block_size; ++i) {
  1792.         const char *sepr = ((i & 3) == 0 && i != 0 ? "  " : " ");
  1793.  
  1794.         dputs(sepr);
  1795.         if (block + i >= bot && block + i < top)
  1796.         dprintf1("%02x", block[i]);
  1797.         else
  1798.         dputs("  ");
  1799.     }
  1800.     dputc('\n');
  1801.     if (as_chars) {
  1802.         debug_indent(indent + strlen(label));
  1803.         for (i = 0; i < block_size; ++i) {
  1804.         byte ch;
  1805.  
  1806.         if ((i & 3) == 0 && i != 0)
  1807.             dputc(' ');
  1808.         if (block + i >= bot && block + i < top &&
  1809.             (ch = block[i]) >= 32 && ch <= 126
  1810.             )
  1811.             dprintf1("  %c", ch);
  1812.         else
  1813.             dputs("   ");
  1814.         }
  1815.         dputc('\n');
  1816.     }
  1817.     }
  1818. #undef block_size
  1819. }
  1820.  
  1821. /* Print one object with the given options. */
  1822. /* Relevant options: type_addresses, no_types, pointers, pointed_strings, */
  1823. /* contents. */
  1824. void
  1825. debug_print_object(const void *obj, const dump_control_t * control)
  1826. {
  1827.     const obj_header_t *pre = ((const obj_header_t *)obj) - 1;
  1828.     ulong size = pre_obj_contents_size(pre);
  1829.     const gs_memory_struct_type_t *type = pre->o_type;
  1830.     dump_options_t options = control->options;
  1831.  
  1832.     dprintf3("  pre=0x%lx(obj=0x%lx) size=%lu", (ulong) pre, (ulong) obj,
  1833.          size);
  1834.     switch (options & (dump_do_type_addresses | dump_do_no_types)) {
  1835.     case dump_do_type_addresses + dump_do_no_types:    /* addresses only */
  1836.         dprintf1(" type=0x%lx", (ulong) type);
  1837.         break;
  1838.     case dump_do_type_addresses:    /* addresses & names */
  1839.         dprintf2(" type=%s(0x%lx)", struct_type_name_string(type),
  1840.              (ulong) type);
  1841.         break;
  1842.     case 0:        /* names only */
  1843.         dprintf1(" type=%s", struct_type_name_string(type));
  1844.     case dump_do_no_types:    /* nothing */
  1845.         ;
  1846.     }
  1847.     if (options & dump_do_marks) {
  1848.     dprintf2(" smark/back=%u (0x%x)", pre->o_smark, pre->o_smark);
  1849.     }
  1850.     dputc('\n');
  1851.     if (type == &st_free)
  1852.     return;
  1853.     if (options & dump_do_pointers) {
  1854.     struct_proc_enum_ptrs((*proc)) = type->enum_ptrs;
  1855.     uint index = 0;
  1856.     enum_ptr_t eptr;
  1857.     gs_ptr_type_t ptype;
  1858.  
  1859.     if (proc != gs_no_struct_enum_ptrs)
  1860.         for (; (ptype = (*proc)(pre + 1, size, index, &eptr, type, NULL)) != 0;
  1861.          ++index
  1862.         ) {
  1863.         const void *ptr = eptr.ptr;
  1864.  
  1865.         dprintf1("    ptr %u: ", index);
  1866.         if (ptype == ptr_string_type || ptype == ptr_const_string_type) {
  1867.             const gs_const_string *str = (const gs_const_string *)ptr;
  1868.  
  1869.             dprintf2("0x%lx(%u)", (ulong) str->data, str->size);
  1870.             if (options & dump_do_pointed_strings) {
  1871.             dputs(" =>\n");
  1872.             debug_dump_contents(str->data, str->data + str->size, 6,
  1873.                         true);
  1874.             } else {
  1875.             dputc('\n');
  1876.             }
  1877.         } else {
  1878.             dprintf1((PTR_BETWEEN(ptr, obj, (const byte *)obj + size) ?
  1879.                   "(0x%lx)\n" : "0x%lx\n"), (ulong) ptr);
  1880.         }
  1881.         }
  1882.     }
  1883.     if (options & dump_do_contents) {
  1884.     debug_dump_contents((const byte *)obj, (const byte *)obj + size,
  1885.                 0, false);
  1886.     }
  1887. }
  1888.  
  1889. /* Print the contents of a chunk with the given options. */
  1890. /* Relevant options: all. */
  1891. void
  1892. debug_dump_chunk(const chunk_t * cp, const dump_control_t * control)
  1893. {
  1894.     dprintf1("chunk at 0x%lx:\n", (ulong) cp);
  1895.     dprintf3("   chead=0x%lx  cbase=0x%lx sbase=0x%lx\n",
  1896.          (ulong) cp->chead, (ulong) cp->cbase, (ulong) cp->sbase);
  1897.     dprintf3("    rcur=0x%lx   rtop=0x%lx  cbot=0x%lx\n",
  1898.          (ulong) cp->rcur, (ulong) cp->rtop, (ulong) cp->cbot);
  1899.     dprintf4("    ctop=0x%lx climit=0x%lx smark=0x%lx, size=%u\n",
  1900.          (ulong) cp->ctop, (ulong) cp->climit, (ulong) cp->smark,
  1901.          cp->smark_size);
  1902.     dprintf2("  sreloc=0x%lx   cend=0x%lx\n",
  1903.          (ulong) cp->sreloc, (ulong) cp->cend);
  1904.     dprintf5("cprev=0x%lx cnext=0x%lx outer=0x%lx inner_count=%u has_refs=%s\n",
  1905.          (ulong) cp->cprev, (ulong) cp->cnext, (ulong) cp->outer,
  1906.          cp->inner_count, (cp->has_refs ? "true" : "false"));
  1907.  
  1908.     dprintf2("  sfree1=0x%lx   sfree=0x%x\n",
  1909.          (ulong) cp->sfree1, cp->sfree);
  1910.     if (control->options & dump_do_strings) {
  1911.     debug_dump_contents((control->bottom == 0 ? cp->ctop :
  1912.                  max(control->bottom, cp->ctop)),
  1913.                 (control->top == 0 ? cp->climit :
  1914.                  min(control->top, cp->climit)),
  1915.                 0, true);
  1916.     }
  1917.     SCAN_CHUNK_OBJECTS(cp)
  1918.     DO_ALL
  1919.     if (obj_in_control_region(pre + 1,
  1920.                   (const byte *)(pre + 1) + size,
  1921.                   control)
  1922.     )
  1923.     debug_print_object(pre + 1, control);
  1924. /* Temporarily redefine gs_exit so a chunk parsing error */
  1925. /* won't actually exit. */
  1926. #define gs_exit(n) DO_NOTHING
  1927.     END_OBJECTS_SCAN
  1928. #undef gs_exit
  1929. }
  1930. void 
  1931. debug_print_chunk(const chunk_t * cp)
  1932. {
  1933.     dump_control_t control;
  1934.  
  1935.     control = dump_control_default;
  1936.     debug_dump_chunk(cp, &control);
  1937. }
  1938.  
  1939. /* Print the contents of all chunks managed by an allocator. */
  1940. /* Relevant options: all. */
  1941. void
  1942. debug_dump_memory(const gs_ref_memory_t * mem, const dump_control_t * control)
  1943. {
  1944.     const chunk_t *mcp;
  1945.  
  1946.     for (mcp = mem->cfirst; mcp != 0; mcp = mcp->cnext) {
  1947.     const chunk_t *cp = (mcp == mem->pcc ? &mem->cc : mcp);
  1948.  
  1949.     if (obj_in_control_region(cp->cbase, cp->cend, control))
  1950.         debug_dump_chunk(cp, control);
  1951.     }
  1952. }
  1953.  
  1954. /* Find all the objects that contain a given pointer. */
  1955. void
  1956. debug_find_pointers(const gs_ref_memory_t *mem, const void *target)
  1957. {
  1958.     dump_control_t control;
  1959.     const chunk_t *mcp;
  1960.  
  1961.     control.options = 0;
  1962.     for (mcp = mem->cfirst; mcp != 0; mcp = mcp->cnext) {
  1963.     const chunk_t *cp = (mcp == mem->pcc ? &mem->cc : mcp);
  1964.  
  1965.     SCAN_CHUNK_OBJECTS(cp);
  1966.     DO_ALL
  1967.         struct_proc_enum_ptrs((*proc)) = pre->o_type->enum_ptrs;
  1968.         uint index = 0;
  1969.         enum_ptr_t eptr;
  1970.  
  1971.         if (proc)        /* doesn't trace refs */
  1972.         for (; (*proc)(pre + 1, size, index, &eptr, pre->o_type, NULL); ++index)
  1973.             if (eptr.ptr == target) {
  1974.             dprintf1("Index %d in", index);
  1975.             debug_print_object(pre + 1, &control);
  1976.             }
  1977. /* Temporarily redefine gs_exit so a chunk parsing error */
  1978. /* won't actually exit. */
  1979. #define gs_exit(n) DO_NOTHING
  1980.     END_OBJECTS_SCAN
  1981. #undef gs_exit
  1982.     }
  1983. }
  1984.  
  1985. #endif /* DEBUG */
  1986.